package com.carter.springframework.core.annotation;

import com.carter.springframework.util.ConcurrentReferenceHashMap;
import com.sun.istack.internal.Nullable;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.Map;

public abstract class AnnotationUtils {
    public static final String VALUE = MergedAnnotation.VALUE;

    private static final AnnotationFilter JAVA_LANG_ANNOTATION_FILTER = AnnotationFilter.packages("java.lang.annotation");

    private static final Map<Class<? extends Annotation>, Map<String, DefaultValueHolder>> defaultValuesCache = new ConcurrentReferenceHashMap<>();

    private static class DefaultValueHolder {

        final Object defaultValue;

        public DefaultValueHolder(Object defaultValue) {
            this.defaultValue = defaultValue;
        }

        @Override
        public String toString() {
            return "*" + this.defaultValue;
        }
    }

    /**
     * 从AnnotatedElement上寻找直接声明的注解
     * @param annotation
     * @param annotationType
     * @param <A>
     * @return
     */
    public static <A extends Annotation> A getAnnotation(Annotation annotation, Class<A> annotationType) {
        // Shortcut: directly present on the element, with no merging needed?
        if (annotationType.isInstance(annotation)) {
            return synthesizeAnnotation((A) annotation, annotationType);
        }
        // Shortcut: no searchable annotations to be found on plain Java classes and core Spring types...
        if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(annotation)) {
            return null;
        }
        // Exhaustive retrieval of merged annotations...
        /**
        return MergedAnnotations.from(annotation, new Annotation[] {annotation}, RepeatableContainers.none())
                .get(annotationType).withNonMergedAttributes()
                .synthesize(AnnotationUtils::isSingleLevelPresent).orElse(null);*/
        return null;
    }

    @Nullable
    public static <A extends Annotation> A getAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) {
        // Shortcut: directly present on the element, with no merging needed?
        if (AnnotationFilter.PLAIN.matches(annotationType) ||
                AnnotationsScanner.hasPlainJavaAnnotationsOnly(annotatedElement)) {
            return annotatedElement.getAnnotation(annotationType);
        }
        // Exhaustive retrieval of merged annotations...
        return MergedAnnotations.from(annotatedElement, MergedAnnotations.SearchStrategy.INHERITED_ANNOTATIONS, RepeatableContainers.none())
                .get(annotationType).withNonMergedAttributes()
                .synthesize(AnnotationUtils::isSingleLevelPresent).orElse(null);
    }

    private static <A extends Annotation> boolean isSingleLevelPresent(MergedAnnotation<A> mergedAnnotation) {
        int distance = mergedAnnotation.getDistance();
        return (distance == 0 || distance == 1);
    }

    public static <A extends Annotation> A synthesizeAnnotation(
            A annotation, @Nullable AnnotatedElement annotatedElement) {

        if (annotation instanceof SynthesizedAnnotation || AnnotationFilter.PLAIN.matches(annotation)) {
            return annotation;
        }
        //return MergedAnnotation.from(annotatedElement, annotation).synthesize();
        return null;
    }

    /**
     * 从AnnotatedElement的层级结构（类或方法）上寻找存在的注解
     * @param method
     * @param annotationType
     * @param <A>
     * @return
     */
    public static <A extends Annotation> A findAnnotation(Method method, @Nullable Class<A> annotationType) {
        if (annotationType == null) {
            return null;
        }
        // 如果不需要对层级结构进行搜索也能找到注解
        if (AnnotationFilter.PLAIN.matches(annotationType) ||
                AnnotationsScanner.hasPlainJavaAnnotationsOnly(method)) {
            return method.getDeclaredAnnotation(annotationType);
        }
        // 注解无法直接获取，需要对层级结构进行搜索
        return MergedAnnotations.from(method, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY, RepeatableContainers.none())
                .get(annotationType).withNonMergedAttributes()
                .synthesize(MergedAnnotation::isPresent).orElse(null);
    }


}
